package com.leyou.trade.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.common.exception.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.trade.dto.OrderFormDTO;
import com.leyou.trade.entity.Order;
import com.leyou.trade.entity.OrderDetail;
import com.leyou.trade.entity.OrderLogistics;
import com.leyou.trade.entity.enums.OrderStatus;
import com.leyou.trade.mapper.OrderMapper;
import com.leyou.trade.service.OrderService;
import com.leyou.trade.utils.PayHelper;
import com.leyou.trade.utils.UserHolder;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.Valid;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.leyou.trade.constants.PayConstants.*;

/**
 * @author 虎哥
 */
@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    private OrderDetailServiceImpl detailService;

    private OrderLogisticsServiceImpl logisticsService;

    private ItemClient itemClient;

    private UserClient userClient;

    private PayHelper payHelper;

    public OrderServiceImpl(OrderDetailServiceImpl detailService, OrderLogisticsServiceImpl logisticsService, ItemClient itemClient, UserClient userClient, PayHelper payHelper) {
        this.detailService = detailService;
        this.logisticsService = logisticsService;
        this.itemClient = itemClient;
        this.userClient = userClient;
        this.payHelper = payHelper;
    }

    @Override
    @Transactional
    public Long createOrder(@Valid OrderFormDTO orderDTO) {
        // 1.写order
        Order order = new Order();
        // 1.1 用户id
        Long userId = UserHolder.getUser();
        order.setUserId(userId);
        // 1.2 金额相关信息
        Map<Long, Integer> carts = orderDTO.getCarts();
        // 1.2.1.获取所有sku的id
        List<Long> idList = new ArrayList<>(carts.keySet());
        // 1.2.2.查询sku
        List<SkuDTO> skuList = itemClient.querySkuByIds(idList);
        // 1.2.3 计算金额的和
        long total = 0;
        for (SkuDTO sku : skuList) {
            // 获取金额
            int num = carts.get(sku.getId());
            // 计算总金额
            total += sku.getPrice() * num;
        }
        // 1.2.4 填写金额数据
        order.setTotalFee(total);
        order.setPaymentType(orderDTO.getPaymentType());
        order.setPostFee(0L);// 全场包邮
        order.setActualFee(total + order.getPostFee()/* - 优惠金额*/);

        // 1.3 订单状态初始化
        order.setStatus(OrderStatus.INIT);

        // 1.4 写order到数据库
        boolean success = save(order);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }

        // 2.写OrderDetail
        // 2.1.定义一个OrderDetail的集合
        List<OrderDetail> details = new ArrayList<>();
        // 2.2.遍历sku集合，转为detail
        for (SkuDTO sku : skuList) {
            // 2.2.1.商品数量
            int num = carts.get(sku.getId());
            // 2.2.2.组装OrderDetail
            OrderDetail detail = new OrderDetail();
            detail.setOrderId(order.getOrderId());
            detail.setImage(StringUtils.substringBefore(sku.getImages(), ","));
            detail.setNum(num);
            detail.setSkuId(sku.getId());
            detail.setSpec(sku.getSpecialSpec());
            detail.setPrice(sku.getPrice());
            detail.setTitle(sku.getTitle());
            // 2.2.3.装入detail集合
            details.add(detail);
        }
        // 2.3.批量新增
        success = detailService.saveBatch(details);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }
        // 3.写orderLogistics
        // 3.1.查询收货地址
        AddressDTO address = userClient.queryAddressById(orderDTO.getAddressId());
        // 3.2.填写物流信息
        OrderLogistics logistics = address.toEntity(OrderLogistics.class);
        logistics.setOrderId(order.getOrderId());
        // 3.3.写入数据库
        success = logisticsService.save(logistics);
        if(!success){
            throw new LyException(500, "订单创建失败！");
        }
        // 返回订单编号
        return order.getOrderId();
    }

    @Override
    public String getPayUrl(Long orderId) {
        // 根据id查询订单
        Order order = getById(orderId);
        // 判断是否存在
        if (order == null) {
            throw new LyException(400, "订单编号错误，订单不存在！");
        }
        // 判断订单状态是否是未付款
        if(order.getStatus() != OrderStatus.INIT){
            // 订单已经关闭或者已经支付，无需再次获取支付链接
            throw new LyException(400, "订单已经支付或者关闭！");
        }
        // TODO 尝试读取redis中的支付url

        // 获取订单金额
        Long actualFee = order.getActualFee();
        // 统一下单，获取支付链接

        String url = payHelper.unifiedOrder(orderId, actualFee);

        // TODO 把支付的url缓存在redis中，2小时有效期

        // 返回支付链接
        return url;
    }

    @Transactional
    public void handleNotify(Map<String, String> data) throws Exception {
        // 1.业务标示校验
        payHelper.checkResultCode(data);
        // 2.签名校验
        payHelper.checkSignature(data);

        // 3.订单状态校验（保证幂等，防止重复通知）
        String outTradeNo = data.get(ORDER_NO_KEY);
        String totalFee = data.get(TOTAL_FEE_KEY);
        if (StringUtils.isBlank(outTradeNo) || StringUtils.isBlank(totalFee)) {
            // 数据有误
            throw new RuntimeException("响应数据有误，订单金额或编号为空！");
        }
        Long orderId = Long.valueOf(outTradeNo);
        Order order = getById(orderId);
        if (!order.getStatus().equals(OrderStatus.INIT)) {
            // 说明订单已经支付过了，属于重复通知，直接返回
            return;
        }

        // 4.订单金额校验
        Long total = Long.valueOf(totalFee);
        if (!total.equals(order.getActualFee())) {
            throw new RuntimeException("订单金额有误，我要报警了！");
        }

        // 5.修改订单状态，更新状态和支付时间两个字段
        update().set("status", OrderStatus.PAY_UP.getValue()).set("pay_time", new Date())
                // 条件包括订单id和订单状态必须为1，乐观锁保证幂等
                .eq("order_id", orderId).eq("status", OrderStatus.INIT.getValue());
        log.info("处理微信支付通知成功！{}", data);
    }

    @Override
    public Integer queryOrderState(Long orderId) {
        // 查询订单
        Order order = getById(orderId);
        // 判断是否存在
        if (order == null) {
            throw new LyException(400, "订单不存在！");
        }
        return order.getStatus().getValue();
    }
}